package cn.yx.common.core.util.security;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.yx.common.core.constant.message.security.HashUtilMessageKey;
import cn.yx.common.core.enums.CharsetEnum;
import cn.yx.common.core.enums.DataEncodeEnum;
import cn.yx.common.core.enums.security.HashAlgorithmEnum;
import cn.yx.common.core.exception.CommonException;
import lombok.extern.slf4j.Slf4j;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * <p>散列算法工具类，使用MD5或SHA算法</p>
 *
 * @author Wgssmart
 */
@Slf4j
public final class HashUtil {

    private HashUtil() {
    }

    /**
     * 不使用盐值的md5加密
     *
     * @param str            明文
     * @param dataEncodeEnum 指定的编码方式
     * @return 使用MD5加密算法得到的密文
     */
    public static String md5(String str, DataEncodeEnum dataEncodeEnum) {
        return hash(str, null, HashAlgorithmEnum.MD5.getValue(), dataEncodeEnum);
    }

    /**
     * 使用盐值的md5加密
     *
     * @param str            明文
     * @param salt           盐值
     * @param dataEncodeEnum 指定的编码方式
     * @return 使用md5加密算法并加入盐值加密得到的密文
     */
    public static String md5(String str, String salt, DataEncodeEnum dataEncodeEnum) {
        return hash(str, salt, HashAlgorithmEnum.MD5.getValue(), dataEncodeEnum);
    }

    /**
     * 不使用盐值的sha1加密
     *
     * @param str            明文
     * @param dataEncodeEnum 指定的编码方式
     * @return 使用sha1加密算法得到的密文
     */
    public static String sha1(String str, DataEncodeEnum dataEncodeEnum) {
        return hash(str, null, HashAlgorithmEnum.SHA1.getValue(), dataEncodeEnum);
    }

    /**
     * 使用盐值的sha1加密
     *
     * @param str            明文
     * @param salt           盐值
     * @param dataEncodeEnum 指定的编码方式
     * @return 使用sha1加密算法并加入盐值加密得到的密文
     */
    public static String sha1(String str, String salt, DataEncodeEnum dataEncodeEnum) {
        return hash(str, salt, HashAlgorithmEnum.SHA1.getValue(), dataEncodeEnum);
    }

    /**
     * 不使用盐值的sha256加密
     *
     * @param str            明文
     * @param dataEncodeEnum 指定的编码方式
     * @return 使用sha1加密算法得到的密文
     */
    public static String sha256(String str, DataEncodeEnum dataEncodeEnum) {
        return hash(str, null, HashAlgorithmEnum.SHA256.getValue(), dataEncodeEnum);
    }

    /**
     * 使用盐值的sha256加密
     *
     * @param str            明文
     * @param salt           盐值
     * @param dataEncodeEnum 指定的编码方式
     * @return 使用sha1加密算法并加入盐值加密得到的密文
     */
    public static String sha256(String str, String salt, DataEncodeEnum dataEncodeEnum) {
        return hash(str, salt, HashAlgorithmEnum.SHA256.getValue(), dataEncodeEnum);
    }

    /**
     * 单向散列，可指定编码方式，如果未指定编码方式，则默认使用十六进制编码
     *
     * @param str            需要加密的明文
     * @param salt           加密所使用的盐值，如果为null或空，则不使用盐值
     * @param algorithm      消息摘要算法名称
     * @param dataEncodeEnum 指定的编码方式
     * @return 通过指定加密算法和盐值得到的密文
     */
    public static String hash(String str, String salt, String algorithm, DataEncodeEnum dataEncodeEnum) {
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] bytes = md.digest((str + (StrUtil.isEmpty(salt) ? "" : salt)).getBytes(CharsetEnum.UTF8.getValue()));
            if (dataEncodeEnum == DataEncodeEnum.HEX) {
                return HexUtil.encodeHexStr(bytes);
            }
            if (dataEncodeEnum == DataEncodeEnum.BASE64) {
                return Base64.encode(bytes);
            }
            return HexUtil.encodeHexStr(bytes);
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
            log.error("hash error: {}, algorithm: {}, data encode: {}", e.getMessage(), algorithm, dataEncodeEnum.getValue());
            throw new CommonException(HashUtilMessageKey.HASH_ERROR, e);
        }
    }

    /**
     * 单向散列，可指定编码方式，如果未指定编码方式，则默认使用十六进制编码
     *
     * @param str               需要加密的明文
     * @param salt              加密所使用的盐值
     * @param hashAlgorithmEnum 消息摘要算法枚举
     * @param dataEncodeEnum    指定的编码方式
     * @return 通过指定加密算法和盐值得到的密文
     */
    public static String hash(String str, String salt, HashAlgorithmEnum hashAlgorithmEnum, DataEncodeEnum dataEncodeEnum) {
        return hash(str, salt, hashAlgorithmEnum.getValue(), dataEncodeEnum);
    }

}
